package com.bt7274.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.bt7274.base.BaseInfoProperties;
import com.bt7274.enums.AnnounceEventType;
import com.bt7274.enums.PromotionType;
import com.bt7274.enums.SortType;
import com.bt7274.mapper.*;
import com.bt7274.pojo.*;
import com.bt7274.pojo.vo.TorrentDetailVO;
import com.bt7274.pojo.vo.TorrentListVO;
import com.bt7274.service.TorrentService;
import com.bt7274.utils.PagedGridResult;
import jakarta.annotation.Resource;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.similarity.UncenteredCosineSimilarity;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import com.github.pagehelper.PageHelper;

import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericPreference;
import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.PreferenceArray;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;

/**
 * @Data 2025/4/13 10:48
 * @Author hp
 * @Version 1.0
 * @Description TorrentServiceImpl
 */

@Service
public class TorrentServiceImpl extends BaseInfoProperties implements TorrentService {
    @Resource
    private TorrentMapper torrentMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private PromotionPolicyMapper promotionPolicyMapper;

    @Resource
    private UserTorrentLikeMapper userTorrentLikeMapper;

    @Resource
    private UserTorrentFavoriteMapper userTorrentFavoriteMapper;

    @Resource
    private TransferHistoryMapper transferHistoryMapper;

    @Resource
    private SettingMapper settingMapper;
    
    @Resource
    private UserTorrentRatingMapper userTorrentRatingMapper;

    @Override
    public Torrent createOrUpdate(@NotNull Torrent torrent) {
        if (torrent.getId() != null) {
            return torrentMapper.updateById(torrent) == 1 ? torrent : null;
        }
        return torrentMapper.insert(torrent) > 0 ? torrent : null;
    }

    @Override
    public Torrent getTorrentByHash(@NotNull String infoHash) {
        infoHash = infoHash.toLowerCase();
        return torrentMapper.selectOne(
                new QueryWrapper<Torrent>().eq("info_hash", infoHash)
        );
    }

    @Override
    public PagedGridResult queryAllReviewed(int page, int pageSize) {
        PageHelper.startPage(page, pageSize);
        List<Torrent> list = torrentMapper.selectList(
                new QueryWrapper<Torrent>().eq("under_review", 1)
        );
        return setterPagedGrid(list, page);
    }

    @Override
    public PagedGridResult queryAllUnreviewed(int page, int pageSize) {
        PageHelper.startPage(page, pageSize);
        List<Torrent> list = torrentMapper.selectList(
                new QueryWrapper<Torrent>().eq("under_review", 0)
        );
        return setterPagedGrid(list, page);
    }

    @Override
    public TorrentDetailVO getTorrentDetail(Long torrentId, String userId) {
        Torrent torrent = torrentMapper.selectById(torrentId);
        if (torrent == null) {
            return null;
        }

        boolean isLiked = userTorrentLikeMapper.selectCount(
                new QueryWrapper<UserTorrentLike>()
                        .eq("user_id", userId)
                        .eq("torrent_id", torrentId)
        ) > 0;

        boolean isFavorited = userTorrentFavoriteMapper.selectCount(
                new QueryWrapper<UserTorrentFavorite>()
                        .eq("user_id", userId)
                        .eq("torrent_id", torrentId)
        ) > 0;

        String sizeStr;
        long size = torrent.getSize();
        if (size < 1024L * 1024) {
            // 小于 1MB，显示 KB
            sizeStr = String.format("%.2fKB", size / 1024.0);
        } else if (size < 1024L * 1024 * 1024) {
            // 小于 1GB，显示 MB
            sizeStr = String.format("%.2fMB", size / (1024.0 * 1024));
        } else {
            // 大于等于 1GB，显示 GB
            sizeStr = String.format("%.2fGB", size / (1024.0 * 1024 * 1024));
        }

        TorrentDetailVO detailVO = new TorrentDetailVO();
        detailVO.setTorrentId(torrentId);
        detailVO.setTitle(torrent.getTitle());
        detailVO.setDescription(torrent.getDescription());
        detailVO.setUploadUser(userMapper.selectById(torrent.getUserId()).getUsername());
        detailVO.setTags(torrent.getTags());
        detailVO.setSize(sizeStr);
        detailVO.setCreateTime(torrent.getCreateTime());
        detailVO.setPromotionPolicy(promotionPolicyMapper.selectById(torrent.getPromotionPolicyId()).getDisplayName());
        detailVO.setLiked(isLiked);
        detailVO.setFavorited(isFavorited);
        detailVO.setStatus(torrent.getStatus());
        return detailVO;
    }

    @Override
    public Torrent getTorrentById(long id) {
        return torrentMapper.selectOne(
                new QueryWrapper<Torrent>().eq("id", id)
        );
    }

    @Override
    public boolean reviewTorrent(String infoHash, int isApproved) {
        Torrent torrent = torrentMapper.selectOne(new QueryWrapper<Torrent>().eq("info_hash", infoHash));
        if (torrent == null) {
            return false;
        }
        torrent.setUnderReview(isApproved == 1);
        return torrentMapper.updateById(torrent) > 0;
    }

    @Override
    public PagedGridResult getTorrentsByCategoryId(Long categoryId, int page, int pageSize) {
        PageHelper.startPage(page, pageSize);
        List<Torrent> list =  torrentMapper.selectList(
                new QueryWrapper<Torrent>().eq("category_id", categoryId).eq("under_review", 1)
        );
        PagedGridResult result = setterPagedGrid(list, page);
        result.setRows(getTorrentListVO((List<Torrent>) result.getRows()));
        return result;
    }

    @Override
    public PagedGridResult searchTorrents(String keyword, SortType sortType, int page, int pageSize) {
        String searchKeyword = "%" + keyword + "%";
        // 提取公共查询条件
        PageHelper.startPage(page, pageSize);
        QueryWrapper<Torrent> queryWrapper = new QueryWrapper<Torrent>()
                .like("tags", searchKeyword)
                .or()
                .like("description", searchKeyword);
        List<Torrent> list = switch (sortType) {
            case TIME -> {
                queryWrapper.orderByDesc("create_time");
                yield torrentMapper.selectList(queryWrapper);
            }
            default -> torrentMapper.selectList(queryWrapper);
        };
        PagedGridResult result = setterPagedGrid(list, page);
        // 使用传统 switch 语句
        List<TorrentListVO> voList = getTorrentListVO((List<Torrent>) result.getRows());
        // 按热度排序
        if (SortType.HOT.equals(sortType)) {
            voList.sort((a, b) ->
                    Integer.compare(b.getUserDownloadCount(), a.getUserDownloadCount())
            );
        }
        result.setRows(voList);
        return result;
    }

    @Override
    public PagedGridResult getUploadedTorrentsByUserId(String userId, int page, int pageSize) {
        PageHelper.startPage(page, pageSize);
        // 查询该用户上传的所有种子
        List<Torrent> torrentList = torrentMapper.selectList(
                new QueryWrapper<Torrent>().eq("user_id", userId)
                        .orderByDesc("create_time")
        );
        return setterPagedGrid(torrentList, page);
    }

    @NotNull
    private List<TorrentListVO> getTorrentListVO(List<Torrent> list) {
        List<TorrentListVO> voList = new ArrayList<>();
        for (Torrent torrent : list) {
            TorrentListVO vo = new TorrentListVO();
            vo.setId(torrent.getId());
            // 通过用户 ID 获取上传用户名
            vo.setUploadUsername(userMapper.selectById(torrent.getUserId()).getUsername());
            vo.setTitle(torrent.getTitle());
            vo.setTags(torrent.getTags());
            vo.setDownloadLink("");
            // 查询点赞总数
            int likeCount = userTorrentLikeMapper.selectCount(
                    new QueryWrapper<UserTorrentLike>().eq("torrent_id", torrent.getId())
            ).intValue();
            vo.setLikeCount(likeCount);

            // 查询用户下载总数
            int userDownloadCount = transferHistoryMapper.selectCount(
                    new QueryWrapper<TransferHistory>().eq("torrent_id", torrent.getId())
            ).intValue();
            vo.setUserDownloadCount(userDownloadCount);

            // 获取促销策略 ID
            Long promotionPolicyId = torrent.getPromotionPolicyId();
            // 根据促销策略 ID 获取促销类型
            PromotionType promotionType = PromotionType.getPromotionTypeByString(promotionPolicyMapper.selectById(promotionPolicyId).getSlug());
            // 为 vo 添加促销信息
            vo.setPromotionType(promotionType.getDescription());
            vo.setStatus(torrent.getStatus());
            voList.add(vo);
        }
        return voList;
    }

    @Override
    public boolean updateTorrentPromotionPolicy(Long torrentId, String promotionPolicySlug) {
        // 检查种子是否存在
        Torrent torrent = torrentMapper.selectOne(
                new QueryWrapper<Torrent>().eq("id", torrentId)
        );
        if (torrent == null) {
            return false;
        }
        torrent.setPromotionPolicyId(promotionPolicyMapper.selectOne(
                new QueryWrapper<PromotionPolicy>().eq("slug", promotionPolicySlug)
            ).getId()
        );
        return torrentMapper.updateById(torrent) > 0;
    }

    @Override
    public boolean deleteTorrentById(Long torrentId) {
        return torrentMapper.deleteById(torrentId) > 0;
    }

    @Override
    public long applyTorrentPromotionUploadOffset(Torrent torrent, long promotionUploadOffset) {
        PromotionPolicy promotionPolicy = promotionPolicyMapper.selectById(torrent.getPromotionPolicyId());
        if (promotionPolicy == null) {
            return promotionUploadOffset;
        }
        return (long) promotionPolicy.applyUploadRatio(promotionUploadOffset);
    }

    @Override
    public long applyTorrentPromotionDownloadOffset(Torrent torrent, long promotionDownloadOffset) {
        PromotionPolicy promotionPolicy = promotionPolicyMapper.selectById(torrent.getPromotionPolicyId());
        if (promotionPolicy == null) {
            return promotionDownloadOffset;
        }
        return (long) promotionPolicy.applyDownloadRatio(promotionDownloadOffset);
    }

    // 用于存储 UUID 到 long 的映射
    private final Map<String, Long> userIdToLongMap = new HashMap<>();
    private final Map<Long, String> longToUserIdMap = new HashMap<>();
    private long nextUserId = 1;

    // 生成唯一的 long 类型用户 ID
    private long getLongUserId(String userId) {
        return userIdToLongMap.computeIfAbsent(userId, k -> {
            long id = nextUserId++;
            longToUserIdMap.put(id, userId);
            return id;
        });
    }

    // 根据 long 类型用户 ID 获取原始 UUID
    private String getOriginalUserId(long longUserId) {
        return longToUserIdMap.get(longUserId);
    }

//    @Override
//    public List<TorrentListVO> recommendTorrentsByMahout(String userId, int limit) throws TasteException {
//        // 构建评分数据
//        DataModel dataModel = buildDataModel();
//
//        // 计算用户相似度
//        UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);
//
//        // 选择最近邻用户
//        UserNeighborhood neighborhood = new NearestNUserNeighborhood(2, similarity, dataModel);
//
//        // 创建推荐器
//        Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);
//
//        // 将 UUID 转换为 long 类型用户 ID
//        long targetUserId = getLongUserId(userId);
//        List<RecommendedItem> recommendations = recommender.recommend(targetUserId, limit);
//
//        List<TorrentListVO> recommendedTorrents = new ArrayList<>();
//        for (RecommendedItem recommendation : recommendations) {
//            long torrentId = recommendation.getItemID();
//            Torrent torrent = torrentMapper.selectById(torrentId);
//            if (torrent != null) {
//                TorrentListVO vo = getTorrentListVO(Collections.singletonList(torrent)).get(0);
//                recommendedTorrents.add(vo);
//            }
//        }
//        return recommendedTorrents;
//    }
//
//    private DataModel buildDataModel() throws TasteException {
//        Map<Long, List<Preference>> userPreferencesMap = new HashMap<>();
//
//        // 处理点赞记录
//        List<UserTorrentLike> likeList = userTorrentLikeMapper.selectList(null);
//        for (UserTorrentLike like : likeList) {
//            long userId = getLongUserId(like.getUserId());
//            long torrentId = like.getTorrentId();
//            addPreference(userPreferencesMap, userId, torrentId, 3);
//        }
//
//        // 处理收藏记录
//        List<UserTorrentFavorite> favoriteList = userTorrentFavoriteMapper.selectList(null);
//        for (UserTorrentFavorite favorite : favoriteList) {
//            long userId = getLongUserId(favorite.getUserId());
//            long torrentId = favorite.getTorrentId();
//            addPreference(userPreferencesMap, userId, torrentId, 3);
//        }
//
//        // 处理做种记录
//        List<TransferHistory> transferHistoryList = transferHistoryMapper.selectList(null);
//        for (TransferHistory transferHistory : transferHistoryList) {
//            long userId = getLongUserId(transferHistory.getUserId());
//            long torrentId = transferHistory.getTorrentId();
//            addPreference(userPreferencesMap, userId, torrentId, 5);
//        }
//
//        List<PreferenceArray> preferenceArrays = new ArrayList<>();
//        for (Map.Entry<Long, List<Preference>> entry : userPreferencesMap.entrySet()) {
//            PreferenceArray preferenceArray = new GenericUserPreferenceArray(entry.getValue());
//            preferenceArrays.add(preferenceArray);
//        }
//
//        return new GenericDataModel((DataModel) preferenceArrays);
//    }
//
//    private void addPreference(Map<Long, List<Preference>> userPreferencesMap, long userId, long torrentId, float value) {
//        userPreferencesMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(new GenericPreference(userId, torrentId, value));
//    }
    @Override
    public List<TorrentListVO> recommendTorrentsByMahout(String userId) throws TasteException {
        List<UserTorrentRating> userList = userTorrentRatingMapper.selectList(null);
        //创建数据模型
        DataModel dataModel = this.createDataModel(userList);
        //获取用户相似程度
        UserSimilarity similarity = new UncenteredCosineSimilarity(dataModel);
        // 计算并输出每两个用户 ID 之间的相似度
        // 获取所有唯一的用户 ID
        Set<Long> uniqueUserIds = userList.stream()
                .map(UserTorrentRating::getUserId)
                .collect(Collectors.toSet());
        List<Long> userIds = new ArrayList<>(uniqueUserIds);
        // 用于记录已经计算过的用户 ID 对
        Set<String> computedPairs = new HashSet<>();
        for (int i = 0; i < userIds.size(); i++) {
            for (int j = i + 1; j < userIds.size(); j++) {
                long userId1 = userIds.get(i);
                long userId2 = userIds.get(j);
                // 生成唯一的键来表示用户 ID 对
                String pairKey = userId1 < userId2 ? userId1 + "-" + userId2 : userId2 + "-" + userId1;
                if (!computedPairs.contains(pairKey)) {
                    double similarityValue = similarity.userSimilarity(userId1, userId2);
                    System.out.printf("用户 %d 与用户 %d 的相似度: %.4f%n", userId1, userId2, similarityValue);
                    computedPairs.add(pairKey);
                }
            }
        }

        //获取用户邻居
        UserNeighborhood userNeighborhood = new NearestNUserNeighborhood(2, similarity, dataModel);

        //构建推荐器
        Recommender recommender = new GenericUserBasedRecommender(dataModel, userNeighborhood, similarity);
        //推荐2个
        long targetUserId = getLongUserId(userId);
        System.out.println("userNeighborhood");
        System.out.println(Arrays.toString(userNeighborhood.getUserNeighborhood(targetUserId)));
        System.out.println("userId: " + targetUserId);
        List<RecommendedItem> recommendedItems = recommender.recommend(targetUserId, Math.toIntExact(torrentMapper.selectCount(null)));
        List<Long> itemIds = recommendedItems.stream().map(RecommendedItem::getItemID).collect(Collectors.toList());
        List<TorrentListVO> recommendedTorrents = new ArrayList<>();
        for (Long itemId : itemIds) {
            System.out.println(itemId);
            Torrent torrent = torrentMapper.selectById(itemId);
            if (torrent != null) {
                TorrentListVO vo = getTorrentListVO(Collections.singletonList(torrent)).get(0);
                recommendedTorrents.add(vo);
            }
        }
        return recommendedTorrents;
    }

    private DataModel createDataModel(List<UserTorrentRating> userArticleOperations) {
        FastByIDMap<PreferenceArray> fastByIdMap = new FastByIDMap<>();
        Map<Long, List<UserTorrentRating>> map = userArticleOperations.stream().collect(Collectors.groupingBy(UserTorrentRating::getUserId));
        Collection<List<UserTorrentRating>> list = map.values();
        for(List<UserTorrentRating> userPreferences : list){
            GenericPreference[] array = new GenericPreference[userPreferences.size()];
            for(int i = 0; i < userPreferences.size(); i++){
                UserTorrentRating userPreference = userPreferences.get(i);
                GenericPreference item = new GenericPreference(userPreference.getUserId(), userPreference.getTorrentId(), userPreference.getValue());
                array[i] = item;
            }
            fastByIdMap.put(array[0].getUserID(), new GenericUserPreferenceArray(Arrays.asList(array)));
        }
        return new GenericDataModel(fastByIdMap);
    }

    @Override
    public void updateTorrentStatus(Long torrentId) {
        // 调用 isTorrentAlive 方法判断种子是否为活种
        boolean isAlive = isTorrentAlive(torrentId);
        Torrent torrent = torrentMapper.selectById(torrentId);
        if (torrent != null) {
            // 假设种子状态为 1 表示活种，0 表示死种
            torrent.setStatus(isAlive ? 1 : 0);
            torrentMapper.updateById(torrent);
        }
    }

    @Override
    public boolean isTorrentAlive(Long torrentId) {
        // 查询该种子的传输历史记录
        List<TransferHistory> transferHistories = transferHistoryMapper.selectList(
                new QueryWrapper<TransferHistory>().eq("torrent_id", torrentId)
        );

        // 遍历传输历史记录，检查 last_event 属性
        for (TransferHistory history : transferHistories) {
            AnnounceEventType eventType = AnnounceEventType.fromName(history.getLastEvent());
            if (eventType == AnnounceEventType.STARTED || eventType == AnnounceEventType.COMPLETED) {
                return true;
            }
        }
        return false;
    }
}
